const { src, dest, parallel, series, watch } = require('gulp');
const { exec } = require('child_process');

const del = require('del');
const browserSync = require('browser-sync');
const sassLint = require('gulp-sass-lint');
const loadPlugins = require('gulp-load-plugins');
const plugins = loadPlugins();
const cwd = process.cwd();
const bs = browserSync.create();

// default config
let config = {
    build: {
        src: 'src',
        dist: 'dist',
        temp: 'temp',
        public: 'public',
        publish: '.publish',
        paths: {
            styles: 'assets/styles/*.scss',
            scripts: 'assets/scripts/*.js',
            pages: '*.html',
            images: 'assets/images/**',
            fonts: 'assets/fonts/**'
        }
    }
}

try {
    const loadConfig = require(`${cwd}/pages.config.js`)
    config = Object.assign({}, config, loadConfig)
} catch (e) {
    console.log(e);
}

const clean = () => {
    // 返回的是一个promise
    return del([config.build.dist, config.build.temp, config.build.publish]);
}

// 单纯做样式检查
const stylelint = () => {
    return src([config.build.paths.styles], { base: config.build.src, cwd: config.build.src })
        .pipe(sassLint())
        .pipe(sassLint.format())
        .pipe(sassLint.failOnError())
}

// 因为sassLint不支持fix选项，所以只能用child Process的形式调用sass-lint-auto-fix
const fixStyle = (done) => {
    return exec('yarn sass-lint-auto-fix -c .sass-lint.yml', (error, stdout, stderr) => {
        if (error) {
            console.error(`exec error: ${error}`);
            return;
        }
        if (stdout) {
            console.log(`stdout: ${stdout}`);
        }
        if (stderr) {
            console.log(`stderr: ${stdout}`);
        }
    });
}

// 检查样式并尽可能做修复
const stylelintFix = series(stylelint, fixStyle);

// 单纯检查js，不做fix
const eslint = () => {
    return src([config.build.paths.scripts], { base: config.build.src, cwd: config.build.src })
        .pipe(plugins.eslint(Object.assign(require(`${cwd}/.eslintrc.json`))))
        .pipe(plugins.eslint.format()).pipe(plugins.eslint.failAfterError())
}

// 检查js且做fix
const eslintFix = () => {
    function isFixed(file) {
        return file.eslint != null && file.eslint.fixed;
    }

    return src([config.build.paths.scripts], { base: config.build.src, cwd: config.build.src })
        .pipe(plugins.eslint(Object.assign(require(`${cwd}/.eslintrc.json`), { fix: true })))
        .pipe(plugins.eslint.format()).pipe(plugins.eslint.failAfterError()).pipe(plugins.if(isFixed, dest(config.build.src)))
}

// 单纯检查(style, js)，不做fix
const lint = parallel(stylelint, eslint);

// 检查并且修复并写回原有的文件
const lintfix = parallel(stylelintFix, eslintFix);

const style = () => {
    return src(config.build.paths.styles, { base: config.build.src, cwd: config.build.src })
        .pipe(plugins.sass({ outputStyle: 'expanded' }))
        .pipe(dest(config.build.temp))
        .pipe(bs.reload({ stream: true }))
}

const script = () => {
    return src(config.build.paths.scripts, { base: config.build.src, cwd: config.build.src })
        .pipe(plugins.babel({ presets: [require('@babel/preset-env')] }))
        .pipe(dest(config.build.temp))
        .pipe(bs.reload({ stream: true }))
}

const page = () => {
    return src(config.build.paths.pages, { base: config.build.src, cwd: config.build.src })
        .pipe(plugins.swig({ data: config.data, defaults: { cache: false } }))
        .pipe(dest(config.build.temp))
        .pipe(bs.reload({ stream: true }))
}

const image = () => {
    return src(config.build.paths.images, { base: config.build.src, cwd: config.build.src })
        .pipe(plugins.imagemin())
        .pipe(dest(config.build.dist))
}

const font = () => {
    return src(config.build.paths.fonts, { base: config.build.src, cwd: config.build.src })
        .pipe(plugins.imagemin())
        .pipe(dest(config.build.dist))
}

const extra = () => {
    return src('**', { base: config.build.public, cwd: config.build.public })
        .pipe(dest(config.build.dist))
}

const parseArgs = () => {
    const args = process.argv;
    const dict = {};
    for (let i = 0; i < args.length; i++) {
        if (args[i].indexOf('--') === 0) {
            dict[args[i].slice(2)] = (args[i + 1] !== undefined && args[i + 1].indexOf('--') === -1) ? args[i + 1] : true;
        }
    }
    return dict;
}

const _serve = () => {
    watch(config.build.paths.styles, { cwd: config.build.src }, style)
    watch(config.build.paths.scripts, { cwd: config.build.src }, series(lintfix, script))
    watch(config.build.paths.pages, { cwd: config.build.src }, page)
    watch([
        config.build.paths.images,
        config.build.paths.fonts
    ], { cwd: config.build.src }, bs.reload)

    watch('**', { cwd: config.build.public }, bs.reload)

    const args = parseArgs();

    bs.init({
        notify: false,
        port: args.port || 8000,
        open: args.hasOwnProperty("open") ? args.open : false,
        // files: 'dist/**',
        server: {
            baseDir: [config.build.temp, config.build.dist, config.build.public],
            routes: {
                '/node_modules': 'node_modules'
            }
        }
    })
}

const useref = () => {
    return src(`${config.build.temp}/${config.build.paths.pages}`, { base: config.build.temp })
        .pipe(plugins.useref({ searchPath: ['temp', '.'] }))
        // html js css
        .pipe(plugins.if(/\.js$/, plugins.uglify()))
        .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
        .pipe(plugins.if(/\.html$/, plugins.htmlmin({
            collapseWhitespace: true,
            minifyCSS: true,
            minifyJS: true
        })))
        .pipe(dest(config.build.dist))
}

const compile = parallel(style, script, page);

const build = series(
    lintfix,
    clean,
    parallel(
        series(compile, useref),
        image,
        font,
        extra
    ),
)

const serve = series(lintfix, compile, _serve);

const startServer = () => {
    const args = parseArgs();
    return src(config.build.dist).pipe(plugins.webserver(Object.assign(config.server, args)))
}

const start = series(build, startServer);

const publish = () => {
    const args = parseArgs();
    if (args.production || args.prod) {
        return src(`${config.build.dist}/**/*`).pipe(plugins.ghPages({
            remoteUrl: config.build.remoteUrl,
            branch: args.b || args.branch || "gh-pages"
        }))
    } else {
        // 如果不是proudction，发布到默认的repo
        return src(`${config.build.dist}/**/*`).pipe(plugins.ghPages())
    }
}

const deploy = series(build, publish);

module.exports = {
    style,
    script,
    page,
    image,
    font,
    extra,
    useref,
    startServer,
    stylelint,
    stylelintFix,
    eslint,
    eslintFix,
    publish,
    // 删除以上   
    clean,
    lint,
    lintfix,
    serve,
    build,
    start,
    deploy
}

// 需要支持的命令
// "clean": "gulp clean",
// "lint": "gulp lint",
// "serve": "gulp serve",
// "build": "gulp build",
// "start": "gulp start",
// "deploy": "gulp deploy --production"